package net.kong.sns.rest.client;

import net.kong.sns.rest.client.cache.EhCacheFactory;
import net.kong.sns.rest.client.cache.KongCacheNames;
import net.kong.sns.rest.client.feed.PublishUserActionMethodInvoke;
import net.kong.sns.rest.client.friend.FriendsRelationMethodInvoke;
import net.kong.sns.rest.client.friend.GetFriendsComplexMethodInvoker;
import net.kong.sns.rest.client.friend.GetFriendsSimpleMethodInvoker;
import net.kong.sns.rest.client.login.LoginMethodInvoke;
import net.kong.sns.rest.client.message.SendMessageMethodInvoke;
import net.kong.sns.rest.client.method.MethodInvokeBase;
import net.kong.sns.rest.client.model.FriendInfo;
import net.kong.sns.rest.client.model.LoginResult;
import net.kong.sns.rest.client.model.Pay4TestRegOrderResponse;
import net.kong.sns.rest.client.model.PayRegOrderResponse;
import net.kong.sns.rest.client.model.UserAuth;
import net.kong.sns.rest.client.model.UserInfo;
import net.kong.sns.rest.client.model.UserRelation;
import net.kong.sns.rest.client.pay.Pay4TestIsCompletedMethodInvoke;
import net.kong.sns.rest.client.pay.Pay4TestRegOrderMethodInvoke;
import net.kong.sns.rest.client.pay.PayIsCompletedMethodInvoke;
import net.kong.sns.rest.client.pay.PayRegOrderMethodInvoke;
import net.kong.sns.rest.client.rest.HttpResponseContent;
import net.kong.sns.rest.client.rest.RestClient;
import net.kong.sns.rest.client.user.LoggedInUserMethodInvoke;
import net.kong.sns.rest.client.user.UserAuthMethodInvoke;
import net.kong.sns.rest.client.user.UserInfoMethodInvoke;
import net.kong.sns.rest.client.user.UsersInfoMethodInvoke;
import net.kong.sns.rest.client.user.UsersStandardInfoInvoke;
import net.kong.org.springframework.util.StringUtils;
import net.kong.org.springframework.util.StopWatch;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;

import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author <a href="mailto:lanhuai@gmail.com">Ning Yubin</a>, <a href="mailto:jolestar@gmail.com">Wang Yuanmin</a>
 * @version $Id: ClientImpl.java,v 1.13.2.5 2010/01/21 03:32:42 wangyuanming Exp $
 */
public class ClientImpl extends RestClient implements Client {

    private static final Logger LOGGER = LoggerFactory.getLogger(ClientFactory.class);

    ClientImpl(String sessionKey, AppKeys appKeysFile) {
        super(appKeysFile.getApiKey(), appKeysFile.getApiSecret(), sessionKey);
    }

    ClientImpl(AppKeys appKeysFile) {
        super(appKeysFile.getApiKey(), appKeysFile.getApiSecret());
    }

    <T> T executeMethod(MethodInvokeBase<T> methodInvoke) {
        HttpResponseContent responseContent = execute(methodInvoke);
        return methodInvoke.parseResponse(responseContent);
    }

    public UserInfo getUserInfo(long userId) {
        UserInfo userInfo;
        StopWatch stopWatch = new StopWatch("Get UserInfo");
        stopWatch.start(String.format("Get Cache[%s]", KongCacheNames.USERINFO.name()));
        Ehcache cache = EhCacheFactory.getInstance().getCache(KongCacheNames.USERINFO.name());
        stopWatch.stop();

        stopWatch.start("Get UserInfo from Cache");
        Element element = cache.get(userId);
        stopWatch.stop();
        if (element != null) {
            userInfo = (UserInfo) element.getObjectValue();
        } else {
            stopWatch.start("Get UserInfo from HTTP");
            userInfo = executeMethod(new UserInfoMethodInvoke(userId, getSessionKey()));
            stopWatch.stop();
            if (userInfo != null) {
                stopWatch.start("Put UserInfo into Cache");
                element = new Element(userId, userInfo);
                cache.put(element);
                stopWatch.stop();
            }
        }
        LOGGER.info(stopWatch.prettyPrint());
        return userInfo;
    }

    public UserInfo getUserInfo(long userId, UserInfo.Field[] fields) {
        Ehcache cache = EhCacheFactory.getInstance().getCache(KongCacheNames.USERINFO.name());
        Element element = cache.get(userId);
        if (element != null) {
            return (UserInfo) element.getObjectValue();
        } else {
            UserInfo userInfo = executeMethod(new UserInfoMethodInvoke(userId, fields, getSessionKey()));
            if (userInfo != null) {
                element = new Element(userId, userInfo);
                cache.put(element);
            }
            return userInfo;
        }
    }

    public Set<UserInfo> getUserInfo(Long[] userIds, UserInfo.Field[] fields) {
        return executeMethod(new UsersInfoMethodInvoke(userIds, fields, getSessionKey()));
    }

    public Long getLoggedInUser() {
        String sess = getSessionKey();
        return new Long(sess.substring(sess.lastIndexOf(".") + 1));
    }

    public Long getLoggedInUserRest() {
        return executeMethod(new LoggedInUserMethodInvoke(getSessionKey()));
    }

    public UserInfo getStandardInfo(long userId) {
        Ehcache cache = EhCacheFactory.getInstance().getCache(KongCacheNames.STANDARD_USERINFO.name());
        Element element = cache.get(userId);
        if (element != null) {
            return (UserInfo) element.getObjectValue();
        } else {
            Set<UserInfo> userInfos = executeMethod(new UsersStandardInfoInvoke(new Long[]{userId}));
            UserInfo userInfo = userInfos != null && userInfos.size() > 0 ? userInfos.iterator().next() : null;
            if (userInfo != null) {
                element = new Element(userId, userInfo);
                cache.put(element);
            }
            return userInfo;
        }
    }

    public Set<UserInfo> getStandardInfo(Long[] userIds, UserInfo.Field[] fields) {
        return executeMethod(new UsersStandardInfoInvoke(userIds, fields));
    }

    public UserAuth isAppUser() {
        Ehcache cache = EhCacheFactory.getInstance().getCache(KongCacheNames.USERAUTH.name());
        Element element = StringUtils.hasText(getSessionKey()) ? cache.get(getSessionKey()) : null;
        if (element != null) {
            return (UserAuth) element.getObjectValue();
        } else {
            Set<UserAuth> userAuthSet = executeMethod(new UserAuthMethodInvoke(getSessionKey()));

            UserAuth userAuth = userAuthSet != null && userAuthSet.size() > 0 ? userAuthSet.iterator().next() : null;
            if (userAuth != null && StringUtils.hasText(getSessionKey())) {
                element = new Element(getSessionKey(), userAuth);
                cache.put(element);
            }
            return userAuth;
        }        
    }

    public Set<UserAuth> isAppUser(Long[] userIds) {
        return executeMethod(new UserAuthMethodInvoke(userIds));
    }

    public List<UserRelation> friends_areFriends(Collection<Long> userIds1, Collection<Long> userIds2) {
        return executeMethod(new FriendsRelationMethodInvoke(getSessionKey(), userIds1, userIds2));
    }

    public List<Long> friends_getAppUsers_id() {
        Ehcache cache = EhCacheFactory.getInstance().getCache(KongCacheNames.FRIENDS_GET_APP_USER_IDS.name());
        Element element = cache.get(getSessionKey());
        if (element != null) {
            return (List<Long>) element.getObjectValue();
        } else {
            List<Long> longs = executeMethod(new GetFriendsSimpleMethodInvoker(KongApiMethod.FRIENDS_GET_APP_USERS, getSessionKey()));

            if (longs != null) {
                element = new Element(getSessionKey(), longs);
                cache.put(element);
            }
            return longs;
        }                
    }

    public List<FriendInfo> friends_getAppUsers_info() {
        Ehcache cache = EhCacheFactory.getInstance().getCache(KongCacheNames.FRIENDS_GET_APP_USER_INFOS.name());
        Element element = cache.get(getSessionKey());
        if (element != null) {
            return (List<FriendInfo>) element.getObjectValue();
        } else {
            List<FriendInfo> friendInfos = executeMethod(new GetFriendsComplexMethodInvoker(KongApiMethod.FRIENDS_GET_APP_USERS, getSessionKey()));

            if (friendInfos != null) {
                element = new Element(getSessionKey(), friendInfos);
                cache.put(element);
            }
            return friendInfos;
        }
    }

    public List<Long> friends_get_id(Long userId) {
        Ehcache cache = EhCacheFactory.getInstance().getCache(KongCacheNames.FRIENDS_GET_IDS.name());
        Element element = cache.get(userId);
        if (element != null) {
            return (List<Long>) element.getObjectValue();
        } else {
            List<Long> longs = executeMethod(new GetFriendsSimpleMethodInvoker(KongApiMethod.FRIENDS_GET, getSessionKey(), userId));
            if (longs != null) {
                element = new Element(userId, longs);
                cache.put(element);
            }
            return longs;
        }
    }

    public List<FriendInfo> friends_get_info(Long userId) {
        Ehcache cache = EhCacheFactory.getInstance().getCache(KongCacheNames.FRIENDS_GET_INFOS.name());
        Element element = cache.get(userId);
        if (element != null) {
            return (List<FriendInfo>) element.getObjectValue();
        } else {
            List<FriendInfo> friendInfos = executeMethod(new GetFriendsComplexMethodInvoker(KongApiMethod.FRIENDS_GET, getSessionKey(), userId));

            if (friendInfos != null) {
                element = new Element(userId, friendInfos);
                cache.put(element);
            }
            return friendInfos;
        }
    }

    public void publishUserAction(final long templateId, final String data, final String equalKey) {
        executorService.submit(new Runnable() {
            public void run() {
                executeMethod(new PublishUserActionMethodInvoke(templateId, data, equalKey, getSessionKey()));
            }
        });
    }

    public void publishUserAction(final long templateId, final String data) {
        executorService.submit(new Runnable() {
            public void run() {
                executeMethod(new PublishUserActionMethodInvoke(templateId, data, getSessionKey()));
            }
        });
    }

    public void sendMessage(final long receiverId, final String content, final Long replyId) {
        executorService.submit(new Runnable() {
            public void run() {
                executeMethod(new SendMessageMethodInvoke(receiverId, content, replyId, getSessionKey()));
            }
        });
    }

    public void sendMessage(final long receiverId, final String content) {
        executorService.submit(new Runnable() {
            public void run() {
                executeMethod(new SendMessageMethodInvoke(receiverId, content, getSessionKey()));
            }
        });
    }

    public PayRegOrderResponse pay_regOrder(long orderId, long amount, PayRegOrderResponse.PayType payType, String desc, String remark1, String remark2) {
        return executeMethod(new PayRegOrderMethodInvoke(orderId, amount, payType, desc, remark1, remark2, getSessionKey()));
    }

    public boolean pay_isCompleted(long orderId) {
        return executeMethod(new PayIsCompletedMethodInvoke(orderId));
    }

    public Pay4TestRegOrderResponse pay4Test_regOrder(long orderId, long amount, Pay4TestRegOrderResponse.PayType payType, String desc, String remark1, String remark2) {
        return executeMethod(new Pay4TestRegOrderMethodInvoke(orderId, amount, payType, desc, remark1, remark2, getSessionKey()));
    }

    public boolean pay4Test_isCompleted(long orderId) {
        return executeMethod(new Pay4TestIsCompletedMethodInvoke(orderId));
    }

    public LoginResult login(String username, String password) {
        return executeMethod(new LoginMethodInvoke(username,password,this.getApiSecret()));
    }
}